#include "pjsipwrapper.h"
#include <string.h>
#include <stdio.h>

//#define USING_TCP
PjApiUaAccountHandler::PjApiUaAccountHandler()
{
    m_account = NULL;
//    m_accountInstance = NULL;
    m_accountCallback = NULL;
}

PjApiUaAccountHandler::~PjApiUaAccountHandler()
{
    if(m_account != NULL)
    {
        delete m_account;
    }

    if(m_accountCallback != NULL)
    {
        delete m_accountCallback;
    }
}

void PjApiUaAccountHandler::setAccount(PjApiUaAccount *account)
{
    m_account = account;
    if (m_account == NULL)
    {
        return;
    }

    configAccount();
}
void PjApiUaAccountHandler::configAccount()
{
    if(m_account == NULL)
    {
        return;
    }

    // to test
    PJ_LOG(3,("test", "config acc"));

    //
    std::string id = "sip:" + m_account->getUsername() + "@" + m_account->getDomain();
    std::string reg = "sip:" + m_account->getRegistar();
    //std::string proxy = "sip:" + m_account->getProxy();
    //printf("id: %s , registar: %s , domain: %s , proxy: %s ,password: %s\n",
    //    id.c_str(), reg.c_str(), m_account->getDomain().c_str()
    //    , m_account->getDomain().c_str(), m_account->getPassword().c_str());
    //Todo

    PJ_LOG(3,("test", "config acc 2"));
    pj_str_t id_ = pj_str((char*)id.c_str());
    pj_str_t reg_uri = pj_str((char*)reg.c_str());

#ifdef USING_TCP
    pj_str_t proxy = pj_str((char*)("sip:" + m_account->getProxy() + ";transport=tcp").c_str());
#else
    pj_str_t proxy = pj_str((char*)("sip:" + m_account->getProxy()).c_str());
#endif
    //pj_str_t proxy =
    pj_str_t username = pj_str((char*)m_account->getUsername().c_str());
    pj_str_t data = pj_str((char*)m_account->getPassword().c_str());
    pj_str_t realm = pj_str((char*)m_account->getDomain().c_str());

    //printf("id: %s , registar: %s , domain: %s , proxy: %s ,password: %s\n",
    //    id_.ptr, reg_uri.ptr, proxy.ptr, proxy.ptr, data.ptr);

    PJSUA_LOCK();
    PjApiUa::getInstance()->setAccountConfig(id_, reg_uri, proxy, username, data, realm);
    PJSUA_UNLOCK();
}

void PjApiUaAccountHandler::setAccountCallback(PjApiUaAccountCallback *callback)
{
    m_accountCallback = callback;
}

void PjApiUaAccountHandler::setTransportID(int id)
{
    m_transportID = id;
}

void PjApiUaAccountHandler::setTransportConfig()
{

    PJSUA_LOCK();
#ifdef USING_TCP
    //

    PjApiUa::getInstance()->setTransportConfig(PJSIP_TRANSPORT_TCP, 5060);
#else
// PJ_LOG(3,("PjApiUaAccountHandler", "\n*******************************************\n!"));
    PjApiUa::getInstance()->setTransportConfig(PJSIP_TRANSPORT_UDP, 5060);
#endif
    PJSUA_UNLOCK();
}

void PjApiUaAccountHandler::setRtpPort(int port)
{
    PJSUA_LOCK();
    PjApiUa::getInstance()->setMediaTransportConfig(port);
    PJSUA_UNLOCK();
}

void PjApiUaAccountHandler::setNullDevice()
{
    PJSUA_LOCK();
    PjApiUa::getInstance()->setNullDevice();
    PJSUA_UNLOCK();
}
void PjApiUaAccountHandler::notifyServerRegistry(bool flag)
{
    if (flag)
    {
       PJ_LOG(3,("PjApiUaAccountHandler", "server registry successfully!"));
    }
    else
    {
        PJ_LOG(3,("PjApiUaAccountHandler", "server registry failed!"));
    }
}

bool PjApiUaAccountHandler::verifyUrl(const std::string url)
{
    if(pjsua_verify_sip_url(url.c_str()) != PJ_SUCCESS)
    {
        return false;
    }
    return true;
}

int PjApiUaAccountHandler::makeCall( const std::string dst_uri,
                void *user_data,
                const std::string content_type,
                const std::string content)
{
    PJ_LOG(3, ("PjApiUaAccountHandler"," start to make call"));
    if(m_account == NULL)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler"," account has not been created"));
        return -1;
    }

    if(m_account->getAccountID() == -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler"," account ID has not been set"));
        pjsua_acc_id id = pjsua_acc_get_default();
        if(id == PJSUA_INVALID_ID)
        {
            PJ_LOG(3, ("PjApiUaAccountHandler"," account ID can not be set"));
            return -1;
        }
        m_account->setAccountID(id);
    }
 PJ_LOG(3, ("PjApiUaAccountHandler"," start to make call 1"));
    pjsua_msg_data msg_data;
    pjsua_msg_data_init(&msg_data);
 PJ_LOG(3, ("PjApiUaAccountHandler"," start to make call 2"));
    if(content_type != "")
    {
        msg_data.content_type = pj_str((char*)content_type.c_str());
    }

    if(content != "")
    {
       // msg_data.msg_body = pj_str((char*)msg_body.c_str());
    }

    pj_status_t status;
    pjsua_call_id call_id;

    PJ_LOG(3, ("PjApiUaAccountHandler"," Can  make call0"));
    //status = pjsua_call_make_call(m_account->getAccountID(), &pj_str((char*)dst_uri.c_str()), 0, user_data, &msg_data, &call_id);
    status = pjsua_call_make_call(m_account->getAccountID(), &pj_str((char*)dst_uri.c_str()), 0, NULL, NULL, &call_id);
    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not make call"));
        return -1;
    }
    PJ_LOG(3, ("PjApiUaAccountHandler"," Make call OK!"));
    return call_id;
}

int PjApiUaAccountHandler::makeCall2( const std::string dst_uri,
                    void *user_data,
                    const pjsua_msg_data *msg_data)
{
    PJ_LOG(3, ("PjApiUaAccountHandler"," start to make call"));
    if(m_account == NULL)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler"," account has not been created"));
        return -1;
    }
    if(m_account->getAccountID() == -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler"," account ID has not been set"));
        pjsua_acc_id id = pjsua_acc_get_default();
        if(id == PJSUA_INVALID_ID)
        {
            PJ_LOG(3, ("PjApiUaAccountHandler"," account ID can not be set"));
            return -1;
        }
        m_account->setAccountID(id);
    }

    pj_status_t status;
    pjsua_call_id call_id;

    PJ_LOG(3, ("PjApiUaAccountHandler"," Can  make call0"));
    status = pjsua_call_make_call(m_account->getAccountID(), &pj_str((char*)dst_uri.c_str()), 0, user_data, msg_data, &call_id);
    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not make call"));
        return -1;
    }
    PJ_LOG(3, ("PjApiUaAccountHandler"," Can  make call"));

    return call_id;
}

int PjApiUaAccountHandler::acceptCall(int call_id)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pj_status_t status = pjsua_call_answer(call_id, 200, 0, 0);
    if(status != PJ_SUCCESS)
    {
        // the error handler
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not accept call"));
        return -1;
    }
    return 0;
}

int PjApiUaAccountHandler::hangupCall(int call_id,
                int code,
                const std::string reason,
                const std::string content_type,
                const std::string content)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pjsua_msg_data msg_data;
    pjsua_msg_data_init(&msg_data);


    if(content_type != "")
    {
        msg_data.content_type = pj_str((char*)content_type.c_str());
    }

    if(content != "")
    {
      //  msg_data->msg_body = pj_str((char*)msg_body.c_str());
    }

    pj_status_t status;
    status = pjsua_call_hangup(call_id, code, &pj_str((char*)reason.c_str()), &msg_data);
    if(status != PJ_SUCCESS)
    {
        // Todo the error handler
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not hangup call"));
        return -1;
    }
    return 0;
}

int PjApiUaAccountHandler::hangupCall2(int call_id,
                int code,
                const std::string reason,
                const pjsua_msg_data *msg_data)
{
    return 0;
}

int PjApiUaAccountHandler::answerCall(int call_id,
                int code,
                const std::string reason,
                const std::string content_type,
                const std::string content)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pjsua_msg_data msg_data;
    pjsua_msg_data_init(&msg_data);


    if(content_type != "")
    {
        msg_data.content_type = pj_str((char*)content_type.c_str());
    }

    if(content != "")
    {
      //  msg_data.msg_body = pj_str((char*)msg_body.c_str());
    }

    pj_status_t status;
    status = pjsua_call_answer(call_id, code, &pj_str((char*)reason.c_str()), &msg_data);
    if(status != PJ_SUCCESS)
    {
        // Todo the error handler
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not answer call"));
        return -1;
    }
    return 0;
}


int PjApiUaAccountHandler::answerCall2(int call_id,
                int code,
                const std::string reason,
                const pjsua_msg_data *msg_data)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }
    pj_status_t status;
    status = pjsua_call_answer(call_id, code, &pj_str((char*)reason.c_str()), msg_data);
    if(status != PJ_SUCCESS)
    {
        // Todo the error handler
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not answer call"));
        return -1;
    }
    return 0;
}

int PjApiUaAccountHandler::holdCall(int call_id, const std::string content_type, const std::string content)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pjsua_msg_data msg_data;
    pjsua_msg_data_init(&msg_data);

    if(content_type != "")
    {
        msg_data.content_type = pj_str((char*)content_type.c_str());
    }

    if(content != "")
    {
       // msg_data.msg_body = pj_str((char*)msg_body.c_str());
    }

    pj_status_t status;
    status = pjsua_call_set_hold(call_id, &msg_data);
    if(status != PJ_SUCCESS)
    {
        // Todo the error handler
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not hold on call"));
        return -1;
    }
    PJ_LOG(3, ("PjApiUaAccountHandler","Hold call OK"));
    return 0;
}

int PjApiUaAccountHandler::resumeCall(int call_id, const std::string content_type, const std::string content)
{
   if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pjsua_msg_data msg_data;
    pjsua_msg_data_init(&msg_data);

    if(content_type != "")
    {
        msg_data.content_type = pj_str((char*)content_type.c_str());
    }

    if(content != "")
    {
      //  msg_data->msg_body = pj_str((char*)msg_body.c_str());
    }

    pj_status_t status;
    status = pjsua_call_reinvite(call_id, 1, &msg_data);
    if(status != PJ_SUCCESS)
    {
        // Todo the error handler
        PJ_LOG(3, ("PjApiUaAccountHandler"," Can not resume call"));
        return -1;
    }
    PJ_LOG(3, ("PjApiUaAccountHandler","Resume call OK"));
    return 0;
}

bool PjApiUaAccountHandler::isCallActive(int call_id)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return false;
    }

    if(pjsua_call_is_active(call_id) == 0)
    {
        return false;
    }
    return true;
}

bool PjApiUaAccountHandler::isCallHasMedia(int call_id)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return false;
    }

    if(pjsua_call_has_media(call_id) == 0)
    {
        return false;
    }
    return true;
}

int PjApiUaAccountHandler::sendIM(const std::string to_uri, const std::string mime_type, const std::string content)
{
    if(m_account == NULL)  return -1;
    if(m_account->getAccountID() == -1)
    {
        pjsua_acc_id id = pjsua_acc_get_default();
        if(id != PJSUA_INVALID_ID)
        {
            m_account->setAccountID(id);
        }
        else
        {
            return -1;
        }
    }

    pj_status_t status = pjsua_im_send(m_account->getAccountID(),
                        &pj_str((char*)to_uri.c_str()),
                        &pj_str((char*)mime_type.c_str()),
                        &pj_str((char*)content.c_str()),
                        0,
                        0);
    if(status != PJ_SUCCESS)
    {
        //pjsua_error();
        return -1;
    }
    return 0;
}

int PjApiUaAccountHandler::sendIMTyping(const std::string to_uri, bool is_typing)
{
    if(m_account == NULL)  return -1;
    if(m_account->getAccountID() == -1)
    {
        pjsua_acc_id id = pjsua_acc_get_default();
        if(id != PJSUA_INVALID_ID)
        {
            m_account->setAccountID(id);
        }
        else
        {
            return -1;
        }
    }
    pj_status_t status;

    if(is_typing)
    {
        status = pjsua_im_typing(m_account->getAccountID(),
                &pj_str((char*)to_uri.c_str()),
                PJ_TRUE,
                0);
    }
    else
    {
        status = pjsua_im_typing(m_account->getAccountID(),
                &pj_str((char*)to_uri.c_str()),
                PJ_FALSE,
                0);
    }

    if(status != PJ_SUCCESS)
    {
        //pjsua_error();
        return -1;
    }

    return 0;
}

int PjApiUaAccountHandler::sendIMTypingInCall(int call_id, bool is_typing)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pj_status_t status;
    status = pjsua_call_send_typing_ind(call_id, is_typing ? 1 : 0, 0);
    if(status != PJ_SUCCESS)
    {
        //Error call back
        return -1;
    }
    PJ_LOG(3, ("PjApiUaAccountHandler","Send IM typing in call OK"));
    return 0;
}

int PjApiUaAccountHandler::sendIMInCall(int call_id, const std::string mime_type, const std::string content)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pj_status_t status;
    status = pjsua_call_send_im(call_id,
                        &pj_str((char*)mime_type.c_str()),
                        &pj_str((char*)content.c_str()),
                        0,
                        0);
    if(status != PJ_SUCCESS)
    {
        //Error call back
        return -1;
    }
    PJ_LOG(3, ("PjApiUaAccountHandler","Send IM typing in call OK"));
    return 0;
}

void PjApiUaAccountHandler::configCallInfoByID(int call_id, PjApiCallInfo **call_info)
{
    (*call_info)->setCallID(call_id);

    pj_status_t status;
    pjsua_call_info *info = (pjsua_call_info *)pj_pool_alloc(pjsua_get_var()->pool,
                                    sizeof(struct pjsua_call_info));

    status = pjsua_call_get_info(call_id, info);

    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3, ("config callinfo","error"));
        return;
    }

    (*call_info)->setAccountID((int)info->acc_id);
    if (info->local_info.slen != 0)
    {
        ((*call_info)->setLocalUri((std::string)(info->local_info.ptr)));
    }

    if (info->local_contact.slen != 0)
    {
        ((*call_info)->setLocalContact((std::string)(info->local_contact.ptr)));
    }

    if (info->remote_info.slen != 0)
    {
        ((*call_info)->setRemoteUri((std::string)(info->remote_info.ptr)));
    }

    if (info->remote_contact.slen != 0)
    {
        ((*call_info)->setRemoteContact((std::string)(info->remote_contact.ptr)));
    }

    if (info->call_id.slen != 0)
    {
        ((*call_info)->setCallIDString((std::string)(info->call_id.ptr)));
    }

    if (info->state_text.slen != 0)
    {
        ((*call_info)->setCallStateText((std::string)(info->state_text.ptr)));
    }

    enum PjApiCallInfo::Role role;
    switch(info->role)
    {
        case PJSIP_ROLE_UAC:
            role = PjApiCallInfo::Caller;
            break;

        case PJSIP_ROLE_UAS:
            role = PjApiCallInfo::Callee;
            break;
            /**
        case PJSIP_UAC_ROLE :
            role = PjApiCallInfo::Caller;
            break;
        case PJSIP_UAS_ROLE:
            role = PjApiCallInfo::Callee;
            break;
            **/
        default:
            role = PjApiCallInfo::Unknown;
            break;
    }

    (*call_info)->setRole(role);

    enum PjApiInviteState::InviteState state;

    switch(info->state)
    {
        case PJSIP_INV_STATE_NULL:
            state = PjApiInviteState::Unknown;
            break;

        case PJSIP_INV_STATE_CALLING:
            state = PjApiInviteState::Calling;
            break;

        case PJSIP_INV_STATE_INCOMING:
            state = PjApiInviteState::Incoming;
            break;

        case PJSIP_INV_STATE_EARLY:
            state = PjApiInviteState::Early;
            break;

        case PJSIP_INV_STATE_CONNECTING:
            state = PjApiInviteState::Connecting;
            break;

        case PJSIP_INV_STATE_CONFIRMED:
            state = PjApiInviteState::Confirmed;
            break;

        case PJSIP_INV_STATE_DISCONNECTED:
            state = PjApiInviteState::Disconnected;
            break;
        default:
            state = PjApiInviteState::Unknown;
            break;

    }

    (*call_info)->setInviteState(state);
    PJ_LOG(3, ("config callinfo","start 5"));
}

int PjApiUaAccountHandler::sendPackage(int call_id, const std::string name, const std::string content_type, const void* content)
{
    if(call_id <= -1)
    {
        PJ_LOG(3, ("PjApiUaAccountHandler","Call ID is not valid"));
        return -1;
    }

    pj_status_t status;

    pj_str_t method = pj_str("INFO");

    pjsua_msg_data msg_data;

    pjsua_msg_data_init(&msg_data);
    if(name != "")
    {
        msg_data.hdr_list.name = pj_str((char*)name.c_str());
    }
    msg_data.msg_body = pj_str((char*)content);
    msg_data.content_type = pj_str((char*)content_type.c_str());

    status = pjsua_call_send_request(call_id, &method, &msg_data);
    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3,("Send package","failed"));
        return -1;
    }
    PJ_LOG(3,("Send package","OK"));

    return 0;

}

int PjApiUaAccountHandler::sendPackageStateless(const std::string target_uri, const std::string from, const std::string to, const std::string contact,
                                const std::string pkg_name, const std::string content_type, const void* content)
{

    pj_status_t status;
    //pjsip_method method;
    //pj_str_t str_method = pj_str("INFO");
    //pjsip_method_init_np(&method, &str_method);

    const pjsip_method method =
    {
        PJSIP_OTHER_METHOD,
        { "INFO2", 99 }
    };

    pjsip_endpoint *endpt;
    //pjsua_msg_data msg_data;
    pjsip_tx_data *tdata;

    endpt = pjsua_get_pjsip_endpt();
   // pjsua_msg_data_init(&msg_data);
   // msg_data.hdr_list.name = pj_str((char*)pkg_name.c_str());

    //pj_str_t name_pj = pj_str((char*)pkg_name.c_str());

    //msg_data.msg_body = pj_str((char*)content);
    pj_str_t data_pj = pj_str((char*)content);

   // msg_data.content_type = pj_str((char*)content_type.c_str());
    pj_str_t content_type_pj = pj_str((char*)content_type.c_str());
    pj_str_t uri_pj = pj_str((char*)target_uri.c_str());
    pj_str_t from_pj = pj_str((char*)from.c_str());
    pj_str_t to_pj = pj_str((char*)to.c_str());
    pj_str_t contact_pj = pj_str((char*)contact.c_str());


    status = pjsip_endpt_create_request(endpt, &method, &uri_pj, &from_pj, &to_pj, &contact_pj,
                                                NULL, 0, &data_pj, &tdata);

   // printf("to: %s, from: %s, data: %s, method: %s", uri_pj.ptr, from_pj.ptr, data_pj.ptr, method.name.ptr);
    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3,("create package","failed"));
        return -1;
    }

    /**
     * Here we set the tdata with the following values
     * 1.service info, that is the service name: Request::Blog::requestID
     * 2.content_type for data
     */
    tdata->info = (char*)pj_pool_alloc(tdata->pool, strlen(pkg_name.c_str())+1);
    pj_memcpy((tdata->info), pkg_name.c_str(), strlen(pkg_name.c_str())+1);
    //tdata->info = pkg_name.c_str();  // using pjsip_tx_data_get_info(rdata) to get this value
    char * p = "/";
//    tdata->msg->body->content_type.type = *pj_strset3(&content_type_pj, &content_type_pj.ptr[0], p);
//    tdata->msg->body->content_type.subtype = *pj_strset2(&content_type_pj, p);

    printf("content type: %s %s %s", tdata->msg->body->content_type.type.ptr,
                            tdata->msg->body->content_type.subtype.ptr,
                            tdata->msg->line.req.method.name.ptr);
  // printf("content type: %s ", tdata->msg->body->content_type.type);

    status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL);

    //status = pjsua_call_send_request(call_id, &method, &msg_data);
    if(status != PJ_SUCCESS)
    {
        PJ_LOG(3,("Send package","failed"));
        return -1;
    }
    PJ_LOG(3,("Send package","OK"));

    return 0;
}
